"""
tests for :mod:`pvlib.iotools.acis`
"""

import pandas as pd
import numpy as np
import pytest
from pvlib.iotools import (
    get_acis_prism, get_acis_nrcc, get_acis_mpe,
    get_acis_station_data, get_acis_available_stations
)
from tests.conftest import RERUNS, RERUNS_DELAY, assert_frame_equal
from requests import HTTPError


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_acis_prism():
    # map_variables=True
    df, meta = get_acis_prism(40.001, -80.001, '2020-01-01', '2020-01-02')
    expected = pd.DataFrame(
        [
            [0.5, 5, 0, 2.5, 0, 62, 0],
            [0, 5, -3, 1, 0, 64, 0]
        ],
        columns=['precipitation', 'temp_air_max', 'temp_air_min',
                 'temp_air_average', 'cooling_degree_days',
                 'heating_degree_days', 'growing_degree_days'],
        index=pd.to_datetime(['2020-01-01', '2020-01-02']),
    )
    assert_frame_equal(df, expected)
    expected_meta = {'latitude': 40, 'longitude': -80, 'altitude': 298.0944}
    assert meta == expected_meta

    # map_variables=False
    df, meta = get_acis_prism(40.001, -80.001, '2020-01-01', '2020-01-02',
                              map_variables=False)
    expected.columns = ['pcpn', 'maxt', 'mint', 'avgt', 'cdd', 'hdd', 'gdd']
    assert_frame_equal(df, expected)
    expected_meta = {'lat': 40, 'lon': -80, 'elev': 298.0944}
    assert meta == expected_meta


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
@pytest.mark.parametrize('grid, expected', [
    (1, [[0.51, 5, 0, 2.5, 0, 62, 0]]),
    (3, [[0.51, 5, -1, 2.0, 0, 63, 0]])
])
def test_get_acis_nrcc(grid, expected):
    # map_variables=True
    df, meta = get_acis_nrcc(40.001, -80.001, '2020-01-01', '2020-01-01', grid)
    expected = pd.DataFrame(
        expected,
        columns=['precipitation', 'temp_air_max', 'temp_air_min',
                 'temp_air_average', 'cooling_degree_days',
                 'heating_degree_days', 'growing_degree_days'],
        index=pd.to_datetime(['2020-01-01']),
    )
    assert_frame_equal(df, expected)
    expected_meta = {'latitude': 40., 'longitude': -80., 'altitude': 356.9208}
    assert meta == pytest.approx(expected_meta)

    # map_variables=False
    df, meta = get_acis_nrcc(40.001, -80.001, '2020-01-01', '2020-01-01', grid,
                             map_variables=False)
    expected.columns = ['pcpn', 'maxt', 'mint', 'avgt', 'cdd', 'hdd', 'gdd']
    assert_frame_equal(df, expected)
    expected_meta = {'lat': 40., 'lon': -80., 'elev': 356.9208}
    assert meta == pytest.approx(expected_meta)


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_acis_nrcc_error():
    with pytest.raises(HTTPError, match='invalid grid'):
        # 50 is not a valid dataset (or "grid", in ACIS lingo)
        _ = get_acis_nrcc(40, -80, '2012-01-01', '2012-01-01', 50)


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_acis_mpe():
    # map_variables=True
    df, meta = get_acis_mpe(40.001, -80.001, '2020-01-01', '2020-01-02')
    expected = pd.DataFrame(
        {'precipitation': [0.4, 0.0]},
        index=pd.to_datetime(['2020-01-01', '2020-01-02']),
    )
    assert_frame_equal(df, expected)
    expected_meta = {'latitude': 40.0083, 'longitude': -79.9653}
    assert meta == expected_meta

    # map_variables=False
    df, meta = get_acis_mpe(40.001, -80.001, '2020-01-01', '2020-01-02',
                            map_variables=False)
    expected.columns = ['pcpn']
    assert_frame_equal(df, expected)
    expected_meta = {'lat': 40.0083, 'lon': -79.9653}
    assert meta == expected_meta


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_acis_station_data():
    # map_variables=True
    df, meta = get_acis_station_data('ORD', '2020-01-10', '2020-01-12',
                                     trace_val=-99)
    expected = pd.DataFrame(
        [[10., 2., 6., np.nan, 21.34, 0., 0., 0., 59., 0.],
         [3., -4., -0.5, np.nan, 9.4, 5.3, 0., 0., 65., 0.],
         [-1., -5., -3., np.nan, -99, -99, 5., 0., 68., 0.]],
        columns=['temp_air_max', 'temp_air_min', 'temp_air_average',
                 'temp_air_observation', 'precipitation', 'snowfall',
                 'snowdepth', 'cooling_degree_days',
                 'heating_degree_days', 'growing_degree_days'],
        index=pd.to_datetime(['2020-01-10', '2020-01-11', '2020-01-12']),
    )
    assert_frame_equal(df, expected)
    expected_meta = {
        'uid': 48,
        'state': 'IL',
        'name': 'CHICAGO OHARE INTL AP',
        'altitude': 204.8256,
        'latitude': 41.96017,
        'longitude': -87.93164
    }
    expected_meta = {
        'valid_daterange': [
            ['1958-11-01', '2023-06-15'],
            ['1958-11-01', '2023-06-15'],
            ['1958-11-01', '2023-06-15'],
            [],
            ['1958-11-01', '2023-06-15'],
            ['1958-11-01', '2023-06-15'],
            ['1958-11-01', '2023-06-15'],
            ['1958-11-01', '2023-06-15'],
            ['1958-11-01', '2023-06-15'],
            ['1958-11-01', '2023-06-15']
        ],
        'name': 'CHICAGO OHARE INTL AP',
        'sids': ['94846 1', '111549 2', 'ORD 3', '72530 4', 'KORD 5',
                 'USW00094846 6', 'ORD 7', 'USW00094846 32'],
        'county': '17031',
        'state': 'IL',
        'climdiv': 'IL02',
        'uid': 48,
        'tzo': -6.0,
        'sid_dates': [
            ['94846 1', '1989-01-19', '9999-12-31'],
            ['94846 1', '1958-10-30', '1989-01-01'],
            ['111549 2', '1989-01-19', '9999-12-31'],
            ['111549 2', '1958-10-30', '1989-01-01'],
            ['ORD 3', '1989-01-19', '9999-12-31'],
            ['ORD 3', '1958-10-30', '1989-01-01'],
            ['72530 4', '1989-01-19', '9999-12-31'],
            ['72530 4', '1958-10-30', '1989-01-01'],
            ['KORD 5', '1989-01-19', '9999-12-31'],
            ['KORD 5', '1958-10-30', '1989-01-01'],
            ['USW00094846 6', '1989-01-19', '9999-12-31'],
            ['USW00094846 6', '1958-10-30', '1989-01-01'],
            ['ORD 7', '1989-01-19', '9999-12-31'],
            ['ORD 7', '1958-10-30', '1989-01-01'],
            ['USW00094846 32', '1989-01-19', '9999-12-31'],
            ['USW00094846 32', '1958-10-30', '1989-01-01']],
        'altitude': 204.8256,
        'longitude': -87.93164,
        'latitude': 41.96017
    }
    # don't check valid dates since they get extended every day
    meta.pop("valid_daterange")
    expected_meta.pop("valid_daterange")
    assert meta == expected_meta

    # map_variables=False
    df, meta = get_acis_station_data('ORD', '2020-01-10', '2020-01-12',
                                     trace_val=-99, map_variables=False)
    expected.columns = ['maxt', 'mint', 'avgt', 'obst', 'pcpn', 'snow',
                        'snwd', 'cdd', 'hdd', 'gdd']
    assert_frame_equal(df, expected)
    expected_meta['lat'] = expected_meta.pop('latitude')
    expected_meta['lon'] = expected_meta.pop('longitude')
    expected_meta['elev'] = expected_meta.pop('altitude')
    meta.pop("valid_daterange")
    assert meta == expected_meta


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_acis_available_stations():
    # use a very narrow bounding box to hopefully make this test less likely
    # to fail due to new stations being added in the future
    lat, lon = 39.8986, -80.1656
    stations = get_acis_available_stations([lat - 0.0001, lat + 0.0001],
                                           [lon - 0.0001, lon + 0.0001])
    assert len(stations) == 1
    station = stations.iloc[0]

    # test the more relevant values
    assert station['name'] == 'WAYNESBURG 1 E'
    assert station['sids'] == ['369367 2', 'USC00369367 6', 'WYNP1 7']
    assert station['state'] == 'PA'
    assert station['altitude'] == 940.
    assert station['tzo'] == -5.0
    assert station['latitude'] == lat
    assert station['longitude'] == lon

    # check that start/end work as filters
    stations = get_acis_available_stations([lat - 0.0001, lat + 0.0001],
                                           [lon - 0.0001, lon + 0.0001],
                                           start='1900-01-01',
                                           end='1900-01-02')
    assert stations.empty
